﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics.PackedVector;

namespace Jemgine.Particles
{
    public class Emitter
    {
        public int MaxParticles { get; set; }
        public int ParticleLifetime { get; set; }
        public int EmissionRate { get; set; }
        public List<Modifier> Modifiers = new List<Modifier>();

        [System.ComponentModel.Editor(typeof(Jemgine.Data.TextureEditor), typeof(System.Drawing.Design.UITypeEditor))]
        public String Texture { get; set; }
        public Distributor Distributor { get; set; }

        public FloatRange ParticleAngle { get; set; }
        public FloatRange ParticleScale { get; set; }

        public enum _BlendMode
        {
            Blend,
            Add
        }

        [ContentSerializer(Optional = true)]
        public _BlendMode BlendMode { get; set; }
        

        internal Particle[] _particles;
        internal int _particleCount = 0;
        internal ParticleVertex[] _particleVerticies;
        internal DynamicVertexBuffer _vertexBuffer;
        internal IndexBuffer _indexBuffer;
        internal Texture2D _texture;
        internal Random _random;

        internal bool Initialized = false;

        public Emitter()
        {
            ParticleAngle = new FloatRange(0, 0);
            ParticleScale = new FloatRange(1, 1);

            EmissionRate = 1;
            MaxParticles = 100;
            ParticleLifetime = 10;

            BlendMode = _BlendMode.Blend;
        }

        public void Initialize(GraphicsDevice Device, ContentManager Content)
        {
            Initialized = false;

            _random = new Random();
            _particleCount = 0;
            _vertexBuffer = null;
            _indexBuffer = null;
            _particles = null;
            _particleVerticies = null;

            if (MaxParticles <= 0) return;
            if (String.IsNullOrEmpty(Texture)) return;

            _texture = Content.Load<Texture2D>(Texture);

            _particles = new Particle[MaxParticles];
            _particleVerticies = new ParticleVertex[MaxParticles * 4];

            for (int i = 0; i < MaxParticles; ++i)
            {
                _particleVerticies[i * 4 + 0].Corner = new Short2(-1, -1);
                _particleVerticies[i * 4 + 1].Corner = new Short2(1, -1);
                _particleVerticies[i * 4 + 2].Corner = new Short2(1, 1);
                _particleVerticies[i * 4 + 3].Corner = new Short2(-1, 1);
            }

            _vertexBuffer = new DynamicVertexBuffer(Device, ParticleVertex.VertexDeclaration,
                                                   MaxParticles * 4, BufferUsage.WriteOnly);

            // Create and populate the index buffer.
            ushort[] indices = new ushort[MaxParticles * 6];

            for (int i = 0; i < MaxParticles; i++)
            {
                indices[i * 6 + 0] = (ushort)(i * 4 + 0);
                indices[i * 6 + 1] = (ushort)(i * 4 + 1);
                indices[i * 6 + 2] = (ushort)(i * 4 + 2);

                indices[i * 6 + 3] = (ushort)(i * 4 + 0);
                indices[i * 6 + 4] = (ushort)(i * 4 + 2);
                indices[i * 6 + 5] = (ushort)(i * 4 + 3);
            }

            _indexBuffer = new IndexBuffer(Device, typeof(ushort), indices.Length, BufferUsage.WriteOnly);
            _indexBuffer.SetData(indices);

            Initialized = true;
        }

        public void UpdateVertexBuffer()
        {
            if (!Initialized) return;

            for (int i = 0; i < _particleCount; ++i)
            {
                _particleVerticies[i * 4 + 0].Angle = _particles[i].angle;
                _particleVerticies[i * 4 + 1].Angle = _particles[i].angle;
                _particleVerticies[i * 4 + 2].Angle = _particles[i].angle;
                _particleVerticies[i * 4 + 3].Angle = _particles[i].angle;

                _particleVerticies[i * 4 + 0].Color = _particles[i].color;
                _particleVerticies[i * 4 + 1].Color = _particles[i].color;
                _particleVerticies[i * 4 + 2].Color = _particles[i].color;
                _particleVerticies[i * 4 + 3].Color = _particles[i].color;

                _particleVerticies[i * 4 + 0].Position = _particles[i].position;
                _particleVerticies[i * 4 + 1].Position = _particles[i].position;
                _particleVerticies[i * 4 + 2].Position = _particles[i].position;
                _particleVerticies[i * 4 + 3].Position = _particles[i].position;

                _particleVerticies[i * 4 + 0].Scale = _particles[i].scale;
                _particleVerticies[i * 4 + 1].Scale = _particles[i].scale;
                _particleVerticies[i * 4 + 2].Scale = _particles[i].scale;
                _particleVerticies[i * 4 + 3].Scale = _particles[i].scale;
            }

            if (_particleCount > 0)
                _vertexBuffer.SetData(_particleVerticies, 0, _particleCount * 4);
            
        }

        public void Emit(Vector2 _position, float _angle)
        {
            if (!Initialized) return;

            if (_particleCount < MaxParticles)
                for (int i = 0; i < EmissionRate && _particleCount < MaxParticles; ++i, ++_particleCount)
                {

                    _particles[_particleCount].age = 0;

                    _particles[_particleCount].angle = _angle + FloatRange.GetValue(ParticleAngle, _random);
                    _particles[_particleCount].scale = FloatRange.GetValue(ParticleScale, _random);

                    _particles[_particleCount].color = Vector4.One;

                    if (Distributor != null)
                        Distributor.SetEmission(_random, ref _particles[_particleCount], _angle);
                    else
                    {
                        _particles[_particleCount].velocity = Vector2.Zero;
                        _particles[_particleCount].position = Vector2.Zero;
                    }

                    _particles[_particleCount].position += _position;
                }
        }

        public void Update(float ElapsedSeconds)
        {
            if (!Initialized) return;

            for (int i = 0; i < _particleCount; )
            {
                _particles[i].age += ElapsedSeconds;
                if (_particles[i].age >= ParticleLifetime)
                {
                    _particles[i] = _particles[_particleCount - 1];
                    --_particleCount;
                }
                else
                    ++i;
            }
            
            for (int M = 0; M < Modifiers.Count; ++M)
                for (int i = 0; i < _particleCount; ++i)
                    Modifiers[M].Apply(ref _particles[i], this, ElapsedSeconds);

            //UpdateVertexBuffer();
        }


        public void ReleaseBuffers()
        {
            if (_vertexBuffer != null)
            {
                _vertexBuffer.Dispose();
                _vertexBuffer = null;
            }

            if (_indexBuffer != null)
            {
                _indexBuffer.Dispose();
                _indexBuffer = null;
            }

            Initialized = false;
        }
    }
}
